package com.xsomnus.Distributed_Architecture.lb.impl;

import com.xsomnus.Distributed_Architecture.lb.AbstractLoadBalancer;
import com.xsomnus.Distributed_Architecture.lb.Node;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author @xsomnus666_xiawenye★
 * @since 2019/7/19 0019 10:55
 * - 才需学也,学需静也/非淡泊无以明志，非宁静无以致远
 * <desc>
 *     活跃调用数越小，表明该服务提供者效率越高，单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。在具体实现中，每个服务提供者对应一个活跃数 active。初始情况下，所有服务提供者活跃数均为0。每收到一个请求，活跃数加1，完成请求后则将活跃数减1。在服务运行一段时间后，性能好的服务提供者处理请求的速度更快，因此活跃数下降的也越快，此时这样的服务提供者能够优先获取到新的服务请求、这就是最小活跃数负载均衡算法的基本思想。除了最小活跃数，LeastActiveLoadBalance 在实现上还引入了权重值。所以准确的来说，LeastActiveLoadBalance 是基于加权最小活跃数算法实现的。举个例子说明一下，在一个服务提供者集群中，有两个性能优异的服务提供者。某一时刻它们的活跃数相同，此时 Dubbo 会根据它们的权重去分配请求，权重越大，获取到新请求的概率就越大。如果两个服务提供者权重相同，此时随机选择一个即可
 * </desc>
 */
public class LeastActiveLoadBalancer extends AbstractLoadBalancer {


    @Override
    protected Node doSelect(List<Node> invokers, String url) {

        int length = invokers.size();

        int leastActive = -1;

        int leastCount = 0;

        int[] leastIndexes = new int[length];

        int[] weights = new int[length];

        int totalWeight = 0;
        int firstWeight = 0;

        boolean sameWeight = true;

        for (int i = 0; i < length; i++) {
            int active = invokers.get(i).getActive();
            int afterWarmup = 0;
            weights[i] = afterWarmup;
            if (leastActive == -1 || active < leastActive) {
                leastActive = active;
                leastCount = 1;
                leastIndexes[0] = i;
                totalWeight = afterWarmup;
                firstWeight = afterWarmup;
                sameWeight = true;
            } else if (active == leastActive) {
                leastIndexes[leastCount++] = i;
                totalWeight += afterWarmup;
                if (sameWeight && i > 0 && afterWarmup != firstWeight) {
                    sameWeight = false;
                }
            }
        }

        if (leastCount == 1) {
            return invokers.get(leastIndexes[0]);
        }

        if (!sameWeight && totalWeight > 0) {
            int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
            for (int i = 0; i < leastCount; i++) {
                int leastIndex = leastIndexes[i];
                offsetWeight -= weights[leastIndex];
                if (offsetWeight < 0) {
                    return invokers.get(leastIndex);
                }
            }
        }
        return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
    }
}
